<?php
namespace app\controller\api;


use app\BaseController;
use app\common\DouYin;
use app\common\WxPayV3;
use think\facade\Db;
use think\facade\Validate;
use think\Response;

class ApiNotify extends BaseController
{
    private string $dyAppID = '';//抖音APPid
    private string $dySalt = '';//抖音支付签名salt
    private string $dyToken = '';//抖音回调验签token


    /*
     * 抖音支付回调
     *
     * */
    public function dy():Response
    {

        $post = input('param.',[]);
        $date = getDateTime('Y-m-d');
        $time = getDateTime('H:i:s');
        $logDir = './log/dy/'.$date;
        if(!is_dir($logDir)){
            mkdir($logDir, 0755, true);
        }
        //记录请求日志
        file_put_contents($logDir.'/douyin_notify.txt',"$date $time ".json_encode($post).PHP_EOL.PHP_EOL,8);

        try{
            /************************************** 请求验证 ***************************************/
            //验证字段
            $rule = [
                'timestamp'  =>  'require|max:12',//Unix 时间戳，字符串类型
                'nonce'  =>  'require|max:4',//随机数
                'msg'  =>  'require',//订单信息的 json 字符串
                'msg_signature'  =>  'require|max:128',//签名
                'type'  =>  'require|max:128',//回调类型标记，支付成功回调为"payment"
            ];

            $validate = Validate::rule($rule);
            if(!$validate->check($post)){
                throw new \Exception($validate->getError());
            }

            //验签
            $signature = DouYin::deSign($post,$this->dyToken);
            if($signature !== $post['msg_signature']){
                throw new \Exception('验签失败，无效的访问');
            }

            //获取业务数据
            $data = json_decode($post['msg'],true);
            if(!$data){
                throw new \Exception('获取业务参数错误');
            }

            if($post['type'] === 'payment'){
                //支付回调

                //验证业务数据
                $payRule = [
                    'appid' => 'require|max:64',//当前交易发起的小程序id
                    'cp_orderno' => 'require|max:64',//商户订单号
                    'cp_extra' => 'require|max:2048',//预下单时开发者传入字段
                    'way' => 'require|max:2',//字段中标识了支付渠道： 1-微信支付，2-支付宝支付，10-抖音支付
                    'channel_no' => 'require|max:128',//支付渠道侧单号(抖音平台请求下游渠道微信或支付宝时传入的单号)
                    'payment_order_no' => 'require|max:128',//支付渠道侧PC单号，支付页面可见(微信支付宝侧的订单号)
                    'total_amount' => 'require|number|between:1,99999999999',//支付金额，单位为分
                    'status' => 'require|max:7',//固定SUCCESS
                    'item_id' => 'require',//订单来源视频对应视频 id
                    'seller_uid' => 'require|max:64',//该笔交易卖家商户号
                    'paid_at' => 'require|number|between:0,9999999999',//支付时间，Unix 时间戳，10 位，整型数
                    'order_id' => 'require|max:64',//抖音侧订单号
                ];
                $validatePay = Validate::rule($payRule);
                if(!$validatePay->check($data)){
                    throw new \Exception($validatePay->getError());
                }

            }elseif($post['type'] === 'refund'){
                //退款回调

                //验证业务数据
                $refundRule = [
                    'appid' => 'require|max:64',//当前交易发起的小程序id
                    'cp_refundno' => 'require|max:64',//商户退款单号
                    'cp_extra' => 'require|max:2048',//预下单时开发者传入字段
                    'message' => 'require|max:356',//退款失败原因描述，详见发起退款错误码
                    'refund_amount' => 'require|number|between:1,99999999999',//退款金额，单位为分
                    'status' => 'require|max:7',//固定SUCCESS
                    'is_all_settled' => 'require',//是否为分账后退款 bool
                    'refund_no' => 'require|max:64',//抖音侧退款单号
                    'refunded_at' => 'require|number|between:0,9999999999',//退款时间，Unix 时间戳，10 位，整型数
                    'order_id' => 'require|max:64',//抖音侧订单号
                ];
                $validateRefund = Validate::rule($refundRule);
                if(!$validateRefund->check($data)){
                    throw new \Exception($validateRefund->getError());
                }
            }

            //验证appId
            if($data['appid'] !== $this->dyAppID){
                throw new \Exception('无效的AppId');
            }

            //验证支付状态
            if($data['status'] !== 'SUCCESS'){
                throw new \Exception('通知结果异常');
            }

            if($post['type'] === 'payment'){
                //支付业务处理
                $outOrderNo = $data['cp_orderno'];
                //验证支付
                if(!DouYin::payQuery($this->dyAppID,$outOrderNo,$this->dySalt)){
                    throw new \Exception("$outOrderNo 当前订单未查询到支付结果");
                }
                //业务处理
                $this->dyPay($data);
            }elseif($post['type'] === 'refund'){
                //退款业务处理
                $outRefundNo = $data['cp_refundno'];
                //验证退款
                if(!DouYin::refundQuery($this->dyAppID,$outRefundNo,$this->dySalt)){
                    throw new \Exception("$outRefundNo 当前订单未查询到退款结果");
                }
                //业务处理
                $this->dyRefund($data);
            }

        }catch (\Exception $e){
            //记录错误日志
            file_put_contents($logDir.'/douyin_error.txt',"$date $time ".$e->getMessage(),8);
        }
        return json(['err_no'=> 0, 'err_tips'=> "success"]);
    }

    /**
     * 抖音支付业务处理
     *
     * @param array $orderDetail 支付详情，详见dy接口业务数据验证
     * @return void
     * */
    private function dyPay(array $orderDetail):void{

    }

    /**
     * 抖音退款业务处理
     *
     * @param array $orderDetail 退款详情，详见dy接口业务数据验证
     * @return void
     * */
    private function dyRefund(array $orderDetail):void
    {
        /************************************** 业务逻辑处理 *****************************************/



    }

    //微信支付异步通知-V3
    public function wxPayV3():Response
    {
        //返回微信接口初始数据WxPayNotifyResults
        $return_msg = 'Ok';
        $return_code = 'SUCCESS';
        //获取通知的数据返回数组
        $request = file_get_contents("php://input");
        $date = date("Y-m-d");
        $time = date("H:i:s");
        $logDir = './log/wx/'.$date;
        if(!is_dir($logDir)){
            mkdir($logDir, 0755, true);
        }
        file_put_contents($logDir.'/wxpay_notify.txt',"$date $time".$request.PHP_EOL.PHP_EOL,8);
        Db::startTrans();
        try {
            if (!$request) {
                //如果没有数据，直接返回失败
                throw new \Exception('no data');
            }

            $timestamp = $this->request->header('wechatpay-timestamp');    //时间戳
            $nonce = $this->request->header('wechatpay-nonce');    //随机字符串
            $signature = $this->request->header('wechatpay-signature');    //签名
            $serialNo = $this->request->header('wechatpay-serial');    //证书序列


            $res = WxPayV3::notify($timestamp,$nonce,$request,$signature,$serialNo);

            if($res['code']){
                throw new \Exception($res['msg']);
            }

            $result = $res['data'];


            //验证回调参数
            if($result['trade_state'] !== 'SUCCESS' || !$result['transaction_id']) {
                throw new \Exception('order error');
            }

            //订单存在且支付
            $out_trade_no_arr = explode('_',$result['out_trade_no']);     //商户订单号
            $transaction_id = $result['transaction_id'];                   //微信订单号
            $total_fee = intval($result['amount']['payer_total']);          //支付金额（分）
            $cash_fee = bc($total_fee,100,4,2);              //支付金额（元）
            $time_end = $result['success_time'];                               //支付时间
            $time_end = $time_end ? strtotime($time_end) : time();             //支付时间戳

            /*****************商户业务逻辑*********************/
            $out_trade_no = array_shift($out_trade_no_arr);

            //订单信息
            $order_info = Db::name('orders')
                ->where('out_trade_no', $out_trade_no)
                ->lock(true)
                ->find();
            if($order_info && $order_info['status'] === 1){

                //$cash_fee = $order_info['pay_money'];//生产环境注释此行

                //验证支付金额是否一致
                if($cash_fee != $order_info['pay_money']){
                    throw new \Exception('支付金额不一致');
                }

                $nt = time();
                $config = Db::name('fund')->column('value','var_name');
                $store = Db::name('store')->field('id,sell_ratio,member_id')->find($order_info['store_id']);
                //平台佣金
                if($store && $order_info['order_type'] < 6){
                    $site_commission = bc($cash_fee,$store['sell_ratio'],3);
                }else{
                    $site_commission = $cash_fee;
                }
                $store_fund = bc($cash_fee,$site_commission,2);
                //更改订单状态
                $order_save = [
                    'id' => $order_info['id'],
                    'transaction_id' => $transaction_id,
                    'status' => 3,
                    'site_commission' => $site_commission,
                    'store_fund' => $store_fund,
                    'pay_time' => $time_end
                ];

                $commission_data = [];
                $m = Db::name('member')->lock(true)->find($order_info['member_id']);

                //商品销售处理
                if($order_info['order_type'] < 6){
                    Db::name('order_goods')->where('orders_id',$order_info['id'])->setField('status',2);
                    //库存处理
                    $goods = Db::name('order_goods')->where('orders_id',$order_info['id'])->select()->toArray();
                    if(count($goods)){
                        foreach ($goods as $g){
                            if($g['goods_item_id']){
                                Db::name('goods_item')
                                    ->where('id',$g['goods_item_id'])
                                    ->dec('sku',$g['num'])
                                    ->inc('total',$g['num'])
                                    ->update();
                                Db::name('goods')
                                    ->where('id',$g['goods_id'])
                                    ->inc('total',$g['num'])
                                    ->update();
                            }else{
                                Db::name('goods')
                                    ->where('id',$g['goods_id'])
                                    ->inc('total',$g['num'])
                                    ->dec('sku',$g['num'])
                                    ->update();
                            }
                        }
                    }
                    //货款处理
                    if($store && $store['member_id']){
                        $commission_data[] = [
                            'member_id' => $store['member_id'],
                            'number' => $store_fund,
                            'type' => 1,
                            'cate' => 5,
                            'child_id' => $m['id'],
                            'orders_id' => $order_info['id'],
                            'status' => 1,
                            'create_time' => $nt,
                            'update_time' => $nt
                        ];
                    }

                    if($order_info['order_type'] == "2"){
                        //拼团处理
                        $group_num = Db::name('goods')->where('id',$order_info['goods_id'])->value('group_num');
                        $old_group = Db::name('orders')
                            ->where('order_type',2)
                            ->where('status',2)
                            ->where('state',0)
                            ->where('goods_id',$order_info['goods_id'])
                            ->order('pay_time')
                            ->column('id');
                        if($old_group + 1 >= $group_num){
                            $oids = [];
                            for($i=0;$i<$group_num-1;$i++){
                                $oids[] = $old_group[$i];
                            }
                            Db::name('orders')->whereIn('id',$oids)->update(['status'=>3,'pt_time'=>0]);
                        }else{
                            $buy_group_time = intval($config['buy_group_time']);
                            $order_save['pt_time'] = strtotime("+$buy_group_time hours");
                            $order_save['status'] = 3;
                        }

                    }
                }

                //充值处理
                if($order_info['order_type'] == "6"){
                    $balance = $order_info['return_money'];
                    $surplus_balance = bc($m['balance'],$balance);
                    $balance_data = [
                        'member_id' => $m['id'],
                        'type' => 1,
                        'cate' => 2,
                        'number' => $balance,
                        'old' => $m['balance'],
                        'surplus' => $surplus_balance,
                        'remark' => '充值。订单号：'.$out_trade_no.' 支付金额：￥'.$order_info['total_money'],
                        'orders_id' => $order_info['id'],
                        'create_time' => $nt,
                    ];
                    Db::name('member_balance')->insert($balance_data);
                    Db::name('member')->where('id',$m['id'])->update(['update_time'=>$nt,'balance'=>$surplus_balance]);
                    $order_save['status'] = 6;
                    $order_save['sign_time'] = strtotime("-".intval($config['buy_sign_day'])." day");//签收日期

                }

                //入驻处理
                /*if($order_info['order_type'] == "7"){
                    //需求不明暂不做处理
                    Db::name('apply')->where('orders_id',$order_info['id'])->update(['state'=>2]);
                    $order_save['status'] = 6;
                }*/
                //佣金处理
                if($m['one_id']){
                    //一级佣金
                    $one = Db::name('member')->find($m['one_id']);
                    if($one && $one['status']){
                        $ratio = $config['yj_one_ratio'];
                        $commission = bc($site_commission,$ratio,3);
                        if($commission > 0){
                            $commission_data[] = [
                                'member_id' => $one['id'],
                                'number' => $commission,
                                'type' => 1,
                                'cate' => 1,
                                'child_id' => $m['id'],
                                'orders_id' => $order_info['id'],
                                'status' => 1,
                                'create_time' => $nt,
                                'update_time' => $nt
                            ];
                        }
                    }

                }
                if($m['two_id']){
                    //二级佣金
                    $two = Db::name('member')->find($m['two_id']);
                    if($two && $two['status']){
                        $ratio = $config['yj_two_ratio'] ?? 0;
                        $commission = bc($site_commission,$ratio,3);
                        if($commission > 0){
                            $commission_data = [
                                'member_id' => $two['id'],
                                'number' => $commission,
                                'type' => 1,
                                'cate' => 2,
                                'child_id' => $m['id'],
                                'orders_id' => $order_info['id'],
                                'status' => 1,
                                'create_time' => $nt,
                                'update_time' => $nt
                            ];

                        }
                    }
                }

                if(count($commission_data)){
                    Db::name('member_commission')->insertAll($commission_data);
                }

                if(!Db::name('orders')->update($order_save)) {
                    throw new \Exception('订单处理失败');
                }
            }else{
                throw new \Exception('order undefined');
            }
            Db::commit();
            $code = 200;
        } catch (\Exception $e){
            Db::rollback();
            $code = 500;
            $msg = $e->getMessage();
            file_put_contents($logDir.'/wxpay_error.txt',"$date $time".$msg.PHP_EOL.PHP_EOL,8);
            $return_code = 'FAIL';
            $return_msg = '参数格式校验错误';
        }
        //返回success给微信支付接口
        return json(['code'=>$return_code,'message'=>$return_msg],$code);
    }

    //微信支付退款通知-V3
    public function wxRefundV3():Response
    {
        //返回微信接口初始数据WxPayNotifyResults
        $return_msg = 'Ok';
        $return_code = 'SUCCESS';
        //获取通知的数据返回数组
        $request = file_get_contents("php://input");
        $date = date("Y-m-d");
        $time = date("H:i:s");
        $logDir = './log/wx/'.$date;
        if(!is_dir($logDir)){
            mkdir($logDir, 0755, true);
        }
        file_put_contents($logDir.'/refund_notify.txt',"$date $time".$request.PHP_EOL.PHP_EOL,8);
        Db::startTrans();
        try {
            if (!$request) {
                //如果没有数据，直接返回失败
                throw new \Exception('no data');
            }

            $timestamp = $this->request->header('wechatpay-timestamp');    //时间戳
            $nonce = $this->request->header('wechatpay-nonce');    //随机字符串
            $signature = $this->request->header('wechatpay-signature');    //签名
            $serialNo = $this->request->header('wechatpay-serial');    //证书序列


            $res = WxPayV3::notify($timestamp,$nonce,$request,$signature,$serialNo);

            if($res['code']){
                throw new \Exception($res['msg']);
            }

            $result = $res['data'];


            //验证回调参数
            if(!isset($result['refund_id'])) {
                throw new \Exception('order error');
            }

            //订单存在且支付
            $refund_id = $result['refund_id'];                   //微信退款订单号
            $time_end = $result['success_time'];                               //支付时间
            $time_end = $time_end ? strtotime($time_end) : time();             //支付时间戳

            /*****************商户业务逻辑*********************/

            //订单信息
            $order_info = Db::name('refund')
                ->where('refund_id', $refund_id)
                ->lock(true)
                ->find();
            if($order_info && $order_info['status'] === 3){

                $status = $result['refund_status'] === 'SUCCESS' ? 2 : 4;
                //更改订单状态
                $order_save = [
                    'id' => $order_info['id'],
                    'status' => $status,
                    'error' => $result['refund_status'],
                    'update_time' => $time_end
                ];

                if(!Db::name('refund')->update($order_save)) {
                    throw new \Exception('退款订单处理失败');
                }
            }else{
                throw new \Exception('order undefined');
            }
            Db::commit();
            $code = 200;
        } catch (\Exception $e){
            Db::rollback();
            $code = 500;
            $msg = $e->getMessage();
            file_put_contents($logDir.'/refund_error.txt',"$date $time".$msg.PHP_EOL.PHP_EOL,8);
            $return_code = 'FAIL';
            $return_msg = '参数格式校验错误';
        }
        //返回success给微信支付接口
        return json(['code'=>$return_code,'message'=>$return_msg],$code);
    }
}
